GtkScale: Fix marks for inverted scales
authorMatthias Clasen <mclasen@redhat.com>
Sun, 15 Jan 2012 19:09:54 +0000 (14:09 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Sun, 15 Jan 2012 19:13:10 +0000 (14:13 -0500)
Problem pointed out by Stefan Sauer in bug 667598. The solution
here is different from his patch. We always draw marks in increasing
direction, and flip the marks and stop positions to match.

gtk/gtkscale.c

index ea253880220025b4561ccc32bf2ec47058562830..fe9f8dfd4e0948560a5e7fb9bcffb112978fba09 100644 (file)
@@ -85,7 +85,7 @@ struct _GtkScalePrivate
 {
   PangoLayout  *layout;
 
-  GList       *marks;
+  GSList       *marks;
 
   gint          digits;
 
@@ -166,6 +166,44 @@ G_DEFINE_TYPE_WITH_CODE (GtkScale, gtk_scale, GTK_TYPE_RANGE,
                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                 gtk_scale_buildable_interface_init))
 
+static gint
+compare_marks (gconstpointer a, gconstpointer b, gpointer data)
+{
+  gboolean inverted = GPOINTER_TO_INT (data);
+  gint val;
+  const GtkScaleMark *ma, *mb;
+
+  val = inverted ? -1 : 1;
+
+  ma = a; mb = b;
+
+  return (ma->value > mb->value) ? val : ((ma->value < mb->value) ? -val : 0);
+}
+
+static void
+gtk_scale_notify (GObject    *object,
+                  GParamSpec *pspec)
+{
+  if (strcmp (pspec->name, "orientation") == 0)
+    {
+      GtkOrientation orientation;
+
+      orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (object));
+      gtk_range_set_flippable (GTK_RANGE (object),
+                               orientation == GTK_ORIENTATION_HORIZONTAL);
+    }
+  else if (strcmp (pspec->name, "inverted") == 0)
+    {
+      GtkScale *scale = GTK_SCALE (object);
+
+      scale->priv->marks = g_slist_sort_with_data (scale->priv->marks,
+                                                   compare_marks,
+                                                   GINT_TO_POINTER (gtk_range_get_inverted (GTK_RANGE (scale))));
+    }
+  else if (G_OBJECT_CLASS (gtk_scale_parent_class)->notify)
+    G_OBJECT_CLASS (gtk_scale_parent_class)->notify (object, pspec);
+}
+
 
 #define add_slider_binding(binding_set, keyval, mask, scroll)              \
   gtk_binding_entry_add_signal (binding_set, keyval, mask,                 \
@@ -186,6 +224,7 @@ gtk_scale_class_init (GtkScaleClass *class)
   
   gobject_class->set_property = gtk_scale_set_property;
   gobject_class->get_property = gtk_scale_get_property;
+  gobject_class->notify = gtk_scale_notify;
   gobject_class->finalize = gtk_scale_finalize;
 
   widget_class->style_updated = gtk_scale_style_updated;
@@ -408,17 +447,6 @@ gtk_scale_class_init (GtkScaleClass *class)
   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_SCALE_ACCESSIBLE);
 }
 
-static void
-gtk_scale_orientation_notify (GtkRange         *range,
-                              const GParamSpec *pspec)
-{
-  GtkOrientation orientation;
-
-  orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (range));
-  gtk_range_set_flippable (range,
-                           orientation == GTK_ORIENTATION_HORIZONTAL);
-}
-
 static void
 gtk_scale_init (GtkScale *scale)
 {
@@ -442,10 +470,8 @@ gtk_scale_init (GtkScale *scale)
   priv->digits = 1;
   gtk_range_set_round_digits (range, priv->digits);
 
-  gtk_scale_orientation_notify (range, NULL);
-  g_signal_connect (scale, "notify::orientation",
-                    G_CALLBACK (gtk_scale_orientation_notify),
-                    NULL);
+  gtk_range_set_flippable (range,
+                           gtk_orientable_get_orientation (GTK_ORIENTABLE (range))== GTK_ORIENTATION_HORIZONTAL);
 
   context = gtk_widget_get_style_context (GTK_WIDGET (scale));
   gtk_style_context_add_class (context, GTK_STYLE_CLASS_SCALE);
@@ -930,7 +956,7 @@ gtk_scale_get_mark_label_size (GtkScale        *scale,
   GtkScalePrivate *priv = scale->priv;
   PangoLayout *layout;
   PangoRectangle logical_rect;
-  GList *m;
+  GSList *m;
   gint w, h;
 
   *count1 = *count2 = 0;
@@ -1054,12 +1080,12 @@ gtk_scale_get_preferred_height (GtkWidget *widget,
 
 static gint
 find_next_pos (GtkWidget       *widget,
-               GList           *list,
+               GSList          *list,
                gint            *marks,
                GtkPositionType  pos)
 {
   GtkAllocation allocation;
-  GList *m;
+  GSList *m;
   gint i;
 
   for (m = list->next, i = 1; m; m = m->next, i++)
@@ -1111,12 +1137,27 @@ gtk_scale_draw (GtkWidget *widget,
       gint x1, x2, x3, y1, y2, y3;
       PangoLayout *layout;
       PangoRectangle logical_rect;
-      GList *m;
+      GSList *m;
       gint min_pos_before, min_pos_after;
       gint min_pos, max_pos;
+      gint n_marks;
 
       orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (range));
-      _gtk_range_get_stop_positions (range, &marks);
+      n_marks = _gtk_range_get_stop_positions (range, &marks);
+      /* We always draw the marks in increasing direction, so flip
+       * the stop positions to match the marks (which we flip in
+       * gtk_scale_notify)
+       */
+      if (gtk_range_get_inverted (range))
+        {
+          for (i = 0; i < n_marks / 2; i++)
+            {
+              x1 = marks[i];
+              marks[i] = marks[n_marks - 1 - i];
+              marks[n_marks - 1 - i] = x1;
+            }
+        }
+
       layout = gtk_widget_create_pango_layout (widget, NULL);
       gtk_range_get_range_rect (range, &range_rect);
 
@@ -1484,8 +1525,10 @@ _gtk_scale_clear_layout (GtkScale *scale)
 }
 
 static void
-gtk_scale_mark_free (GtkScaleMark *mark)
+gtk_scale_mark_free (gpointer data)
 {
+  GtkScaleMark *mark = data;
+
   g_free (mark->markup);
   g_free (mark);
 }
@@ -1508,8 +1551,7 @@ gtk_scale_clear_marks (GtkScale *scale)
 
   priv = scale->priv;
 
-  g_list_foreach (priv->marks, (GFunc)gtk_scale_mark_free, NULL);
-  g_list_free (priv->marks);
+  g_slist_free_full (priv->marks, gtk_scale_mark_free);
   priv->marks = NULL;
 
   context = gtk_widget_get_style_context (GTK_WIDGET (scale));
@@ -1521,16 +1563,6 @@ gtk_scale_clear_marks (GtkScale *scale)
   gtk_widget_queue_resize (GTK_WIDGET (scale));
 }
 
-static gint
-compare_marks (gpointer a, gpointer b)
-{
-  GtkScaleMark *ma, *mb;
-
-  ma = a; mb = b;
-
-  return (ma->value > mb->value) ? 1 : ((ma->value < mb->value) ? -1 : 0);
-}
-
 /**
  * gtk_scale_add_mark:
  * @scale: a #GtkScale
@@ -1563,7 +1595,7 @@ gtk_scale_add_mark (GtkScale        *scale,
 {
   GtkScalePrivate *priv;
   GtkScaleMark *mark;
-  GList *m;
+  GSList *m;
   gdouble *values;
   gint n, i;
   GtkStyleContext *context;
@@ -1582,14 +1614,14 @@ gtk_scale_add_mark (GtkScale        *scale,
   else
     mark->position = GTK_POS_BOTTOM;
 
-  priv->marks = g_list_insert_sorted (priv->marks, mark,
-                                      (GCompareFunc) compare_marks);
+  priv->marks = g_slist_insert_sorted (priv->marks, mark,
+                                       (GCompareFunc) compare_marks);
 
 #define MARKS_ABOVE 1
 #define MARKS_BELOW 2
 
   all_pos = 0;
-  n = g_list_length (priv->marks);
+  n = g_slist_length (priv->marks);
   values = g_new (gdouble, n);
   for (m = priv->marks, i = 0; m; m = m->next, i++)
     {
@@ -1636,7 +1668,7 @@ typedef struct
 {
   GtkScale *scale;
   GtkBuilder *builder;
-  GList *marks;
+  GSList *marks;
 } MarksSubparserData;
 
 typedef struct
@@ -1752,7 +1784,7 @@ marks_start_element (GMarkupParseContext *context,
       mark->context = g_strdup (msg_context);
       mark->translatable = translatable;
 
-      parser_data->marks = g_list_prepend (parser_data->marks, mark);
+      parser_data->marks = g_slist_prepend (parser_data->marks, mark);
     }
   else
     {
@@ -1834,7 +1866,7 @@ gtk_scale_buildable_custom_finished (GtkBuildable *buildable,
 
   if (strcmp (tagname, "marks") == 0)
     {
-      GList *m;
+      GSList *m;
       gchar *markup;
 
       marks_data = (MarksSubparserData *)user_data;
@@ -1855,7 +1887,7 @@ gtk_scale_buildable_custom_finished (GtkBuildable *buildable,
           mark_data_free (mdata);
         }
 
-      g_list_free (marks_data->marks);
+      g_slist_free (marks_data->marks);
       g_slice_free (MarksSubparserData, marks_data);
     }
 }